/** @brief Define common functions for multiple test cases. 
 * 
 *  testCommon.cpp
 *  kangmodb
 *
 *  Created by 강모 김 on 11. 4. 24..
 *  Copyright 2011 강모소프트. All rights reserved.
 *
 */

#include "testCommon.h"
#include "kangmodb.h"
#include "table.h"

/**
 * @brief Create a database, create a table in it, begin a transaction, open a cursor on the table.
 */
KD_VOID setup_cursor(
	stgKangmoDB ** db,
	stgTransaction ** tx,
	stgTableId * tableId,
	stgTableCursor ** cursor )
{
    KD_TRY
	{
		*db = new stgKangmoDB();
		KD_ASSERT( *db != NULL );
		
		// Destroy the database object
		KD_CALL( (*db)->initialize( KD_STRING("Setup_logfile.dat") ) );
		
		// Drop the database if any
		(*db)->dropDatabase();
	
		// create succeeds
		KD_CALL( (*db)->createDatabase() );

		// open succeeds
		KD_CALL( (*db)->openDatabase() );

		// begin transaction succeeds
		KD_CALL( (*db)->beginTransaction( tx ) );
		// tx should NOT be NULL
		KD_ASSERT( *tx != NULL );

		if ( tableId != NULL )
		{
			{
				// create table succeeds
				KD_CALL( (*db)->createTable( *tx, tableId) );
			}
			// commit transaction
			KD_CALL( (*db)->commitTransaction( *tx ) );
			
			if ( cursor != NULL )
			{
				// open table succeeds
				KD_CALL( (*db)->openTable( *tx, *tableId, cursor) );
			}
		}
	} 
	KD_CATCH
	KD_FINALLY
	KD_END
}

/**
 * @brief Close the open cursor, commit the transaction, drop the table, drop the database.
 */
KD_VOID teardown_cursor(
	stgKangmoDB ** db,
	stgTransaction ** tx,
	stgTableId * tableId,
	stgTableCursor ** cursor )
{
	KD_TRY
	{
		if ( tableId != NULL )
		{
			if ( cursor != NULL )
			{
				// close table succeeds
				KD_CALL( (*db)->closeTable( *tx, *cursor) );

				// commit transaction
				KD_CALL( (*db)->commitTransaction( *tx ) );		
			}

			{
				// drop table succeeds
				KD_CALL( (*db)->dropTable( *tx, *tableId) );
			}
			// commit transaction
			KD_CALL( (*db)->commitTransaction( *tx ) );		
		}
		
		// end transaction
		KD_CALL( (*db)->endTransaction( *tx ) );

		*tx = NULL;
		
		// close succeeds
		KD_CALL( (*db)->closeDatabase() );
		
		// Drop succeeds because the database is not open
		KD_CALL( (*db)->dropDatabase() );
		
		// Destroy the database object
		KD_CALL( (*db)->destroy() );

		delete *db;
		
		*db = NULL;
	} 
	KD_CATCH
	KD_FINALLY
	KD_END
}


/** @brief Verify if multiple keys have expected values with a stgTableCursor object
 */
KD_VOID verify_key_values(stgTableCursor * cursor, int loopCount, const char * keyFormat, const char * valueFormat)
{
	KD_ASSERT( cursor != NULL );
	KD_ASSERT( loopCount > 0 );
	KD_ASSERT( keyFormat != NULL );
	KD_ASSERT( valueFormat != NULL );

	char key[256];
	char value[256];
	
	KD_TRY
	{
	    for (int i=0; i<loopCount; i++)
	    {
		    stgData selectedData; 
		    sprintf( key, keyFormat, i);
		    KD_CALL( cursor->seekRecord( stgKey(key), & selectedData ) );
		    sprintf( value, valueFormat, i);
		    KD_ASSERT( util::string_compare(value, strlen(value), (const char*)selectedData.getData(), selectedData.getLength()) == 0);
	    }
	}
	KD_CATCH
	KD_FINALLY
	KD_END
}

/** @brief verify if a key have an expected value with a stgTableCursor object
 */
KD_VOID verify_key_value(stgTableCursor * cursor, const char * key, const char * expectedValue)
{
	KD_ASSERT( cursor != NULL );
	KD_ASSERT( key != NULL );
	KD_ASSERT( expectedValue != NULL );
	
	KD_TRY
	{
	    stgData selectedData; 
	    KD_CALL( cursor->seekRecord( stgKey(key), & selectedData ) );
	    KD_ASSERT( util::string_compare(expectedValue, strlen(expectedValue), (const char *)selectedData.getData(), selectedData.getLength()) == 0);
	}
	KD_CATCH
	KD_FINALLY
	KD_END
}

/** @brief Verify if multiple keys have expected values with a stgTable object
 */
KD_VOID verify_table_key_values(const stgVersion viewVersion, stgTable * table, int loopCount, const char * keyFormat, const char * valueFormat)
{
	KD_ASSERT( table != NULL );
	KD_ASSERT( loopCount > 0 );
	KD_ASSERT( keyFormat != NULL );
	KD_ASSERT( valueFormat != NULL );

	char key[256];
	char value[256];
	stgDataDesc * dataDesc;
	
	KD_TRY
	{
	    for (int i=0; i<loopCount; i++)
	    {
		    stgData selectedData; 
		    sprintf( key, keyFormat, i);
			KD_CALL( table->getDataDesc(key, &dataDesc) );
		    KD_CALL( table->seekData( viewVersion, dataDesc, & selectedData ) );
		    sprintf( value, valueFormat, i);
		    KD_ASSERT( util::string_compare(value, strlen(value), (const char *)selectedData.getData(), selectedData.getLength()) == 0);
	    }
	}
	KD_CATCH
	KD_FINALLY
	KD_END
}

/** @brief verify if a key have an expected value with a stgTable object
 */
KD_VOID verify_table_key_value(const stgVersion viewVersion, stgTable * table, const char * key, const char * expectedValue)
{
	KD_ASSERT( table != NULL );
	KD_ASSERT( key != NULL );
	KD_ASSERT( expectedValue != NULL );

	stgDataDesc * dataDesc;
	
	KD_TRY
	{
	    stgData selectedData;
		KD_CALL( table->getDataDesc(key, &dataDesc) );
	    KD_CALL( table->seekData( viewVersion, dataDesc, & selectedData ) );
	    KD_ASSERT( util::string_compare(expectedValue, strlen(expectedValue), (const char *)selectedData.getData(), selectedData.getLength()) == 0);
	}
	KD_CATCH
	KD_FINALLY
	KD_END
}

/** @brief Allocate a new data version with a given string data.
 */
KD_VOID allocVersion(const char * strData, stgDataVersion ** dataVersion)
{
	KD_TRY
	{
		int len = strlen(strData);
		int allocSize = STG_DATA_VERSION_SIZE(len);
		stgDataVersion * v1 = (stgDataVersion*) malloc( allocSize );
		KD_ASSERT( v1 != NULL );

		initDataVersion( v1 );
	
		*dataVersion = v1;
	}
	KD_CATCH
	KD_FINALLY
	KD_END
}

/** @brief Free the given data version.
 */
KD_VOID freeVersion(stgDataVersion * dataVersion)
{
	KD_TRY
	{
		free(dataVersion);
	}
	KD_CATCH
	KD_FINALLY
	KD_END
}

/** @brief Check if a data version has the same content with the given string.
 */
bool isDataEqual( stgDataVersion * dataVersion, const char * dataStr)
{
	KD_ASSERT( strlen(dataVersion->data) == dataVersion->dataLength );
	KD_ASSERT( strlen(dataStr) == dataVersion->dataLength );
	return strncmp( dataVersion->data, dataStr, dataVersion->dataLength) == 0;
}


/** @brief Check if a data version has the same content with the given string.
 */
bool isDataEqual( stgData * data, const char * dataStr)
{
	KD_ASSERT( strlen((const char*)data->getData()) == data->getLength() );
	KD_ASSERT( strlen(dataStr) == data->getLength() );
	return strncmp( (const char*)data->getData(), dataStr, data->getLength()) == 0;
}
